/*******************************************************************************
 * Copyright (c) 2011 Irineu Martins de Lima Moura (imlm2).
 * This file is part of JBeat.
 * 
 *     JBeat is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 * 
 *     JBeat is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Lesser General Public License for more details.
 * 
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with JBeat.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Contributors:
 *     Irineu Martins de Lima Moura (imlm2) - initial API and implementation
 ******************************************************************************/
package br.ufpe.cin.imlm2.util;

import java.util.List;
import java.util.regex.Pattern;

/**
 * Utility class used to query for properties based on naming conventions and/or rules of the Java
 * and JVM language.
 * @author Irineu
 *
 */
public class Naming {

	private static final String IDENTIFIER_REGEX = "[\\p{L}$_]+[\\p{L}$_\\d]*";
	private static final Pattern VALID_JVM_PACKAGE_NAME = Pattern.compile("("+IDENTIFIER_REGEX+")" +
			"(\\."+ IDENTIFIER_REGEX +")+");

	/**
	 * Tells whether or not the given name is a valid JVM package name.
	 * Coincidentally this function can also tell if the given name is a valid fully qualified type
	 * name.
	 * @param name
	 * @return
	 */
	public static boolean isValidJVMName(String name){
		return VALID_JVM_PACKAGE_NAME.matcher(name).matches();
	}

	/**
	 * Creates a pattern from which one can match against other type names to check
	 * if they are part of the package with the name passed as a parameter.
	 * If <code>packageName</code> ends with '.*' the resulting pattern will also match types
	 * that are in subpackages of this package.<br><br>
	 * Example:<br>
	 * <code>
	 * Pattern p = Naming.createPatternFromPackageName("java.lang.*")<br>
	 * System.out.println(p.matcher("java.lang.String").matches()); <br>
	 * System.out.println(p.matcher("java.lang.reflect.Method").matches()); <br>
	 * System.out.println(p.matcher("java.util.ArrayList").matches()); <br><br>
	 * </code>
	 * 
	 *  Prints: <br><br>
	 *  
	 *  true <br>
	 *  true <br>
	 *  false <br>
	 * @param packageName
	 * @return a valid pattern to match against type names, null in case <code>packageName</code> is
	 * not a valid package name.
	 */
	public static  Pattern createPatternFromPackageName(String packageName){		
		String prefix = packageName;
		if(packageName.endsWith(".*")){
			 prefix = packageName.substring(0, packageName.length() - 2);
		}
		if(isValidJVMName(prefix)) {
			String[] parts = packageName.split("\\.");
			StringBuffer pattStr = new StringBuffer();
			for (String s : parts) {
				pattStr.append(s+"\\.");
			}
			pattStr.delete(pattStr.length() - 2, pattStr.length());
			if(packageName.endsWith(".*")){
				pattStr.append("(\\."+ IDENTIFIER_REGEX +")+");
			} else {
				pattStr.append("[\\p{L}$_]+[\\p{L}$_\\d]*");
			}
			return Pattern.compile(pattStr.toString());
		} else {
			return null;
		}
	}
	
	/**
	 * Utility method that tells whether or not the type with name <code>fullyQualifiedTypeName</code>
	 *  is in one of the packages or subpackages represented by the patterns in the patterns list. 
	 *  Although this method is independent of how the pattern was created it is preferably to pass a 
	 *  list with patterns created using <code>Naming.createPatternFromPackageName(String)</code>. 
	 * 
	 * @param fullyQualifiedTypeName
	 * @param pats
	 * @return
	 */
	public static boolean isTypeInPackageList(String fullyQualifiedTypeName,List<Pattern> pats){
		for (Pattern pattern : pats) {
			if(pattern.matcher(fullyQualifiedTypeName).matches()) return true;
		}
		return false;
	}
	
	/**
	 * Tries to determine if a method is synthetic solely based on naming conventions.
	 * Coding relying on this method should be careful as the values returned may be inaccurate if
	 * the methodName doesn't follow any convention or follow an unknown convention. 
	 * @param methoName
	 * @return
	 */
	public static boolean isMethodSynthetic(String methoName){
		//TODO IMPROVE THIS
		return (methoName.startsWith("$SWITCH_TABLE")) || methoName.equals("<clinit>");
	}

}
